
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>


/* 开启线程安全 */
#define THREAD_SAFE

/* 是否加头 */
#define ENABLE_HEAD

/* 使能轮转 */
#define ENABLE_ROTATE

/* ms时间戳 */
#define TIMESTAMP_MS

/* 开启多用户文件管理 */
#define ENABLE_USR_MG


/* 管理结构体 */
typedef struct log_mg_s {
	int  file_fd;
	char file_path[64];
	int  had_init;
	int  monitor;
	char module[20];
	int  cur_offset;
	int  tot_msgcnt;
	int  max_length;
	pthread_mutex_t lock;
} log_mg_s;

/* 最大管理数, 含默认id和其他用户id */
#ifdef ENABLE_USR_MG
 #define MG_MAX 11
#else
 #define MG_MAX 1
#endif

#define MG_DEF 0 /* 默认管理id一个 */
#define MG_USR 1 /* 用户管理id起始 */

#define LINE_SIZE   1024 /* 一行最大字节数 */

/* 管理数据 */
log_mg_s log_mg[MG_MAX] = { { 0 } };

/* 当前时间戳 */
char *now_time_str(char *time_str, int str_len)
{
	//static char time_str[64];
	struct timespec ts;
	struct tm *tm_ptr;
	int time_str_len;

	clock_gettime(CLOCK_REALTIME, &ts);
	tm_ptr = localtime(&ts.tv_sec);
	time_str_len = strftime(time_str, str_len, "%Y-%m-%d %H:%M:%S", tm_ptr);
	time_str[time_str_len] = '\0';

	#ifdef TIMESTAMP_MS
	time_str_len += snprintf(time_str + time_str_len, str_len - time_str_len, ".%03ld", ts.tv_nsec / 1000000);
	time_str[time_str_len] = '\0';
	#endif

	return time_str;
}

/* log头的格式 */
#define LOG_HEAD_FORMAT_LINE1 	"### CUR_OFFSET:%10d, TIMESTAMP:%24s ###\n"
#define LOG_HEAD_FORMAT_LINE2 	"### MAX_LENGTH:%10d, LOG_MODULE:%23s ###\n"
#define LOG_HEAD_FORMAT  		(LOG_HEAD_FORMAT_LINE1 LOG_HEAD_FORMAT_LINE2)

/* log头长度 */
int log_head_len()
{
	static int head_len = 0;
	char buf[512];

	#ifdef ENABLE_HEAD
	if(head_len <= 0)
		head_len = snprintf(buf, 512, LOG_HEAD_FORMAT, 0, "a", 0, "a");
	#endif

	return head_len;
}

/* log头当前偏移字段 */
int log_head_cur_oft(int fd)
{
	char head_str[256];
	log_mg_s tmp_mg;
	char time_str[64];

	lseek(fd, 0, SEEK_SET);
	read(fd, head_str, log_head_len());
	if(2 == sscanf(head_str, LOG_HEAD_FORMAT_LINE1, &tmp_mg.cur_offset, &time_str[0]))
	{
		return tmp_mg.cur_offset;
	}

	return 0;
}

/* log头载入 */
int log_head_load(int mg_i)
{
    log_mg_s *tgt_mg_p = &log_mg[mg_i];
    
    tgt_mg_p->cur_offset = log_head_cur_oft(tgt_mg_p->file_fd);
    return 0;
}

/* log头写入 */
int log_head_write(int mg_i, char *time_str)
{
	char head_str[256];
	log_mg_s *tgt_mg_p = &log_mg[mg_i];

	snprintf(head_str, 256, LOG_HEAD_FORMAT, tgt_mg_p->cur_offset, time_str, tgt_mg_p->max_length, tgt_mg_p->module);
	lseek(tgt_mg_p->file_fd, 0, SEEK_SET);
	write(tgt_mg_p->file_fd, head_str, log_head_len());

	return 0;
}

/* log数据写入 */
int log_data_write(int mg_i, char *time_str, char *msg, char *lvl)
{
	char a_log[LINE_SIZE];
	int  a_log_len;	
	log_mg_s *tgt_mg_p = &log_mg[mg_i];

	/* 格式化数据 */
	a_log_len = snprintf(a_log, LINE_SIZE, "%s %s", time_str, msg);
	lseek(tgt_mg_p->file_fd, log_head_len() + tgt_mg_p->cur_offset, SEEK_SET);
	write(tgt_mg_p->file_fd, a_log, a_log_len);
	//fsync(tgt_mg_p->file_fd);
	tgt_mg_p->cur_offset += a_log_len;
	tgt_mg_p->tot_msgcnt ++;

	/* 限制数据总长度 */
	if(tgt_mg_p->cur_offset > tgt_mg_p->max_length)
	{
		#ifdef ENABLE_ROTATE
		 ftruncate(tgt_mg_p->file_fd, log_head_len() + tgt_mg_p->cur_offset);
		 tgt_mg_p->cur_offset = 0;
		#else
		 return -1;
		#endif
	}

	if(1 == tgt_mg_p->monitor)
	{
		printf("%s", a_log);
		fflush(stdout);
	}
	return 0;
}

/* 根据路径创建文件夹 */
int log_mkdir(char *file_path)
{
    char cmd_str[128];
    char dir_path[64];
    char *dir_end = NULL;
    int  len;
    
    if(NULL == file_path)
        return -1;

    dir_end = strrchr(file_path, '/');
    if(NULL == dir_end)
        return 0;

    len = dir_end - file_path + 1;
    if(len > (sizeof(dir_path) - 2))
        return -2;

    memcpy(dir_path, file_path, len);
    dir_path[len] = 0;

    snprintf(cmd_str, 128, "mkdir -p -m 755 %s", dir_path);
    system(cmd_str);
    return 1;
}

/* log初始化 */
int _rlog_init(int mg_i, char *file_path, char *module, int max_length)
{
	int fd;
	log_mg_s *tgt_mg_p = &log_mg[mg_i];

	if(1 == tgt_mg_p->had_init)
		return 0;

    /* 创建文件夹 */
    log_mkdir(file_path);
    
	/* 打开文件 */
	fd = open(file_path, O_RDWR | O_CREAT | O_NONBLOCK, 0777);
	if(fd <= 0)
		return -1;

	/* 设置管理信息 */
	tgt_mg_p->file_fd = fd;
	tgt_mg_p->monitor = 0;
	tgt_mg_p->cur_offset = 0;
	tgt_mg_p->max_length = max_length;
	memcpy(tgt_mg_p->module, module, 20);
	memcpy(tgt_mg_p->file_path, file_path, 64);
	#ifdef THREAD_SAFE
	 pthread_mutex_init(&tgt_mg_p->lock, NULL);
	#endif

	tgt_mg_p->had_init = 1;

	#ifdef ENABLE_HEAD
	/* 载入头信息 */
	log_head_load(mg_i);
	#endif
	return 0;
}

int rlog_init(char *file_path, char *module, int max_length)
{
    return _rlog_init(MG_DEF, file_path, module, max_length);
}

/* log退出 */
int _rlog_deinit(int mg_i)
{
    log_mg_s *tgt_mg_p = &log_mg[mg_i];
    
	if(0 == tgt_mg_p->had_init)
		return -1;

	if(tgt_mg_p->file_fd <= 0)
		return -2;

    close(tgt_mg_p->file_fd);
    tgt_mg_p->had_init = 0;
    return 0;
}

int rlog_deinit(void)
{
    return _rlog_deinit(MG_DEF);
}

/* log监控 */
int _rlog_mon(int mg_i, int on)
{
    log_mg_s *tgt_mg_p = &log_mg[mg_i];

	if(0 == tgt_mg_p->had_init)
		return 0;

	tgt_mg_p->monitor = on;
	return tgt_mg_p->monitor;
}
int rlog_mon(int on)
{
    return _rlog_mon(MG_DEF, on);
}

/* log输出 */
int _rlog_msg(int mg_i, char *msg)
{
	char time_str[64];
	log_mg_s *tgt_mg_p = &log_mg[mg_i];

	if(0 == tgt_mg_p->had_init)
		return -1;

	#ifdef THREAD_SAFE
	pthread_mutex_lock(&tgt_mg_p->lock);
	#endif

	/* 生成时间戳 */
	now_time_str(time_str, 64);

	/* 写数据 */
	log_data_write(mg_i, time_str, msg, NULL);

	#ifdef ENABLE_HEAD
	/* 更新头 */
	log_head_write(mg_i, time_str);
	#endif

	#ifdef THREAD_SAFE
	pthread_mutex_unlock(&tgt_mg_p->lock);
	#endif

    return 0;
}

int rlog_msg(char *msg)
{
    return _rlog_msg(MG_DEF, msg);
}

/* log格式化输出 */
int rlog_fmt(char *format, ...)
{
	char buf[LINE_SIZE];
	int  len;
	va_list args;

	va_start(args, format);
	len = vsnprintf(buf, LINE_SIZE, format, args);
	va_end(args);

	if(len <= 0)
		return -1;

	_rlog_msg(MG_DEF, buf);
	return 0;
}

/* log显示，传入fd、行数、打印行数，便于外部查看调用 */
int rlog_show_fd(int fd, int lines, void (*print_fun)(char *fmt, ...))
{
	int  i, log_total, cur_offset;
	char a_log[LINE_SIZE], c;
	size_t a_log_off, r_len;
	int line = 0, cr_num = 0;

	/* 获取log状态 */
	log_total = lseek(fd, 0, SEEK_END);
	cur_offset = log_head_cur_oft(fd);

	/* 遍历log */
	a_log_off = LINE_SIZE - 3;
	for(i = 0; i < (log_total - log_head_len() + 1); i++)
	{
		/* log有翻转，从尾部继续找 */
		if((cur_offset - i - 1) < 0)
			lseek(fd, log_total - i + cur_offset - 1, SEEK_SET); /* 文件尾向前 */
		else
			lseek(fd, log_head_len() + cur_offset - i - 1, SEEK_SET); /* 当前位置向前 */

		/* 读取一个字符 */
		r_len = read(fd, &c, 1);
		if(r_len != 1)	return -2;
		if(c == 0)		continue;

		/* 匹配换行 */
		if(c == '\n')
		{
			cr_num++;
			if(cr_num == 2)
			{
				/* 显示指定行数后返回 */
				if(line++ >= lines)
					return 0;

				/* 设置换行和结束符，显示一行log */
				a_log[LINE_SIZE - 2] = '\n';
				a_log[LINE_SIZE - 1] = 0;
				print_fun("%s", a_log + a_log_off + 1);
				fflush(stdout);

				/* 重设单行log数据偏移，继续查找 */
				cr_num = 1;
				a_log_off = LINE_SIZE - 3;
			}
		}
		else
		{
			/* 记录单行log数据 */
			if(a_log_off < 0)
				return -3;
			else
				a_log[a_log_off--] = c;
		}
	}

	return 0;

}

/* log显示 */
int _rlog_show(int mg_i, int lines)
{
    log_mg_s *tgt_mg_p = &log_mg[mg_i];

	/* 判断初始化 */
	if(0 == tgt_mg_p->had_init)
		return -1;

    /* 参数非法默认输出20行 */
    if(lines <= 0)  lines = 20;

    /* 打印 */
	return rlog_show_fd(tgt_mg_p->file_fd, lines, (void (*)(char *, ...))printf);
}
int rlog_show(int lines)
{
    return _rlog_show(MG_DEF, lines);
}

#ifdef ENABLE_USR_MG
/* 
 * 基于用户文件的接口
 */

/* 用户接口默认模块名和长度 */
#define USR_MOD_DEF "USR"
#define USR_MAX_DEF (512 * 1024)

/* 通过文件路径查找管理数据 */
int _rlog_mg_get(char *file_path)
{
    int mg_i = MG_USR;
    log_mg_s *tgt_mg_p = NULL;

    for(mg_i = MG_USR; mg_i < MG_MAX; mg_i++)
    {
        tgt_mg_p = &log_mg[mg_i];
        if(0 == strcmp(tgt_mg_p->file_path, file_path))
            return mg_i;
    }

    return MG_MAX;
}

/* 通过文件路径测试管理数据, 没有初始化 */
int _rlog_mg_tst(char *file_path)
{
    int mg_i = MG_USR;
    log_mg_s *tgt_mg_p = NULL;

    /* 已存在直接返回 */
    mg_i = _rlog_mg_get(file_path);
    if(mg_i < MG_MAX) return mg_i;

    /* 没有初始化 */
    for(mg_i = MG_USR; mg_i < MG_MAX; mg_i++)
    {
        tgt_mg_p = &log_mg[mg_i];
        if(0 == tgt_mg_p->had_init)
        {
            _rlog_init(mg_i, file_path, USR_MOD_DEF, USR_MAX_DEF);
            return mg_i;
        }
    }

    /* 找不到输出到默认MG */
    return MG_DEF;
}

/* 用户log监控 */
int rlog_usr_mon(char *file_path, int on)
{
    int mg_i;

    mg_i = _rlog_mg_tst(file_path);
    return _rlog_mon(mg_i, on);
}

/* 用户log显示 */
int rlog_usr_show(char *file_path, int lines)
{
    int mg_i;

    mg_i = _rlog_mg_tst(file_path);
    return _rlog_show(mg_i, lines);
}

/* 用户log输出 */
int rlog_usr_msg(char *file_path, char *msg)
{
    int mg_i;

    mg_i = _rlog_mg_tst(file_path);
    return _rlog_msg(mg_i, msg);
}

/* 用户log格式化输出 */
int rlog_usr_fmt(char *file_path, char *format, ...)
{
    char buf[1024];
    int  len, mg_i;
    va_list args;

    mg_i = _rlog_mg_tst(file_path);

    va_start(args, format);
    len = vsnprintf(buf, 1024, format, args);
    va_end(args);

    if(len <= 0)
        return -1;

    return _rlog_msg(mg_i, buf);
}

/* 用户log重新初始化 */
int rlog_usr_reinit(char *file_path, char *module, int max_len)
{
    int mg_i, rc;
    char *new_mod = (NULL == module) ? USR_MOD_DEF : module;  /* 为空不修改 */
    int   new_len = (0   == max_len) ? USR_MAX_DEF : max_len; /* 为0不修改 */

    mg_i = _rlog_mg_tst(file_path);

    /* 重新初始化 */
    _rlog_deinit(mg_i);
    rc = _rlog_init(mg_i, file_path, new_mod, new_len);

    /* 写一条log, 更新信息, 且记录下信息更改 */
    rlog_usr_fmt(file_path, "reinit: new mod %s, new len %d\r\n", new_mod, new_len);
    return rc;
}
#endif

#if TEST
int main(int argc, char **argv)
{
	int i, max = atoi(argv[1]);
	char *test_log[] = {"a hi debug!", "b hi info!", "c hi!"};
	char a_log[128];

	rlog_init("my_log.log", "test", 1024);
	rlog_mon(1);
	for(i = 0; i < max; i++)
	{
		sprintf(a_log, "%d %s", i, test_log[i%3]);
		rlog_msg(a_log);

		rlog_fmt("fmt %d %s", i, test_log[i%3]);
	}

	return 0;
}
#endif
